using System;
using System.Reflection;


// This class is used to demonstrate reflection
// It has a field, constructor, method, property, and an event
internal sealed class SomeType {
   private Int32 m_someField;
   public SomeType(ref Int32 x) { x *= 2; }
   public override String ToString() { return m_someField.ToString(); }
   public Int32 SomeProp {
      get { return m_someField; }
      set { 
         if (value < 1) 
            throw new ArgumentOutOfRangeException(
               "value", "value must be > 0");
         m_someField = value;
      }
   }
   public event EventHandler SomeEvent;
   private void NoCompilerWarnings() {
      SomeEvent.ToString();
   }
}

public sealed class Program {
   private const BindingFlags c_bf = BindingFlags.DeclaredOnly |
      BindingFlags.Public | BindingFlags.NonPublic |
      BindingFlags.Instance;

   public static void Main() {
      Type t = typeof(SomeType);
      UsingInvokeMemberToBindAndInvokeTheMember(t);
      Console.WriteLine();
      BindingToMemberFirstAndThenInvokingTheMember(t);
   }

   private static void UsingInvokeMemberToBindAndInvokeTheMember(Type t) {
      Console.WriteLine("UsingInvokeMemberToBindAndInvokeTheMember");

      // Construct an instance of the Type
      Object[] args = new Object[] { 12 };  // Constructor arguments
      Console.WriteLine("x before constructor called: " + args[0]);
      Object obj = t.InvokeMember(null,
         c_bf | BindingFlags.CreateInstance, null, null, args);
      Console.WriteLine("Type: " + obj.GetType().ToString());
      Console.WriteLine("x after constructor returns: " + args[0]);

      // Read and write to a field
      t.InvokeMember("m_someField",
         c_bf | BindingFlags.SetField, null, obj, new Object[] { 5 });
      Int32 v = (Int32)t.InvokeMember("m_someField",
         c_bf | BindingFlags.GetField, null, obj, null);
      Console.WriteLine("someField: " + v);

      // Call a method
      String s = (String)t.InvokeMember("ToString",
         c_bf | BindingFlags.InvokeMethod, null, obj, null);
      Console.WriteLine("ToString: " + s);

      // Read and write a property
      try {
         t.InvokeMember("SomeProp",
            c_bf | BindingFlags.SetProperty, null, obj, new Object[] { 0 });
      }
      catch (TargetInvocationException e) {
         if (e.InnerException.GetType() != typeof(ArgumentOutOfRangeException))
            throw;
         Console.WriteLine("Property set catch.");
      }
      t.InvokeMember("SomeProp",
         c_bf | BindingFlags.SetProperty, null, obj, new Object[] { 2 });
      v = (Int32)t.InvokeMember("SomeProp",
         c_bf | BindingFlags.GetProperty, null, obj, null);
      Console.WriteLine("SomeProp: " + v);

      // NOTE: InvokeMember doesn't support events
   }


   private static void BindingToMemberFirstAndThenInvokingTheMember(Type t) {
      Console.WriteLine("BindingToMemberFirstAndThenInvokingTheMember");

      // Construct an instance
      ConstructorInfo ctor = t.GetConstructor(
         new Type[] { Type.GetType("System.Int32&") });
      Object[] args = new Object[] { 12 };  // Constructor arguments
      Console.WriteLine("x before constructor called: " + args[0]);
      Object obj = ctor.Invoke(args);
      Console.WriteLine("Type: " + obj.GetType().ToString());
      Console.WriteLine("x after constructor returns: " + args[0]);

      // Read and write to a field
      FieldInfo fi = obj.GetType().GetField("m_someField", c_bf);
      fi.SetValue(obj, 33);
      Console.WriteLine("someField: " + fi.GetValue(obj));

      // Call a method
      MethodInfo mi = obj.GetType().GetMethod("ToString", c_bf);
      String s = (String) mi.Invoke(obj, null);
      Console.WriteLine("ToString: " + s);

      // Read and write a property
      PropertyInfo pi = 
         obj.GetType().GetProperty("SomeProp", typeof(Int32));
      foreach (MethodInfo m in pi.GetAccessors())
         Console.WriteLine(m);
      try {
         pi.SetValue(obj, 0, null);
      } 
      catch (TargetInvocationException e) {
         if (e.InnerException.GetType() != 
            typeof(ArgumentOutOfRangeException)) throw;
         Console.WriteLine("Property set catch.");
      }
      pi.SetValue(obj, 2, null);
      Console.WriteLine("SomeProp: " + pi.GetValue(obj, null));

      // Add and remove a delegate from the event
      EventInfo ei = obj.GetType().GetEvent("SomeEvent", c_bf);
      Console.WriteLine("AddMethod: "        + ei.GetAddMethod());
      Console.WriteLine("RemoveMethod: "     + ei.GetRemoveMethod());
      Console.WriteLine("EventHandlerType: " + ei.EventHandlerType);

      EventHandler ts = new EventHandler(EventCallback);
      ei.AddEventHandler(obj, ts);
      ei.RemoveEventHandler(obj, ts);
   }

   private static void EventCallback(Object sender, EventArgs e) {}
}